feat(publish): check whether a minted ID has already been used - #669
Open
ashleycaselli wants to merge 1 commit into
Open
feat(publish): check whether a minted ID has already been used#669ashleycaselli wants to merge 1 commit into
ashleycaselli wants to merge 1 commit into
Conversation
An identifier minted under a fixed prefix -- a new space's IRI, say -- carries no artifact code, so nothing makes it unique: the same form filled with the same name twice yields the same IRI, and the second nanopublication silently extends the first one's resource instead of defining a new one. A nanopublication cannot be edited afterwards, so the collision is worth catching before publishing rather than after. The publish form now asks the query API whether any identifier it minted is already introduced, and refuses to publish (or to build a preview, which the preview page publishes without coming back through the form) naming the one that is taken. Which identifiers those are is knowable only from how the value was built, so TemplateContext records them as it mints them. Four kinds are deliberately left alone: - an IRI typed out in full, which names an existing thing rather than minting one -- "Defining an open-ended Space with existing URI" exists precisely to do that; - one minted under the new nanopublication's own namespace, whose artifact code is substituted at signing time; - one built by an auto-escaping placeholder, where the IRI is derived from the text itself: two people writing the same AIDA sentence are meant to arrive at the same IRI, so an existing one is agreement, not a collision; - supersede and override, where keeping the source's identifier is the point (docs/fill-modes.md). The carve-outs are drawn from the published templates rather than guessed: of the templates combining nt:IntroducedResource with nt:hasPrefix, every one mints a new resource except the AIDA family, which is exactly the auto-escaping kind. A query service that cannot be reached answers "not taken": a check that cannot be made is not evidence of a collision, and publishing should not depend on the query services being up. Closes #646 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K55p8T6uQoSv48C5SXueve
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #646
The problem
When a new ID is minted in a way that doesn't include the artifact code of the nanopublication — defining a new space, say — nothing makes it unique. Filling the same form with the same name twice yields the same IRI, and the second nanopublication silently extends the first one's resource instead of defining a new one. A nanopublication can't be edited afterwards, so the mistake is only undone by a corrected version.
This is not hypothetical:
https://w3id.org/spaces/example/barcurrently has two introducing nanopublications, the second published while this was being written.The change
The publish form asks the query API (
get-introducing-nanopub, on the indexed meta repo) whether any identifier it minted is already introduced, and refuses to publish — naming the one that is taken — before signing. The same check guards the preview button, since the preview page publishes the nanopublication it was given without coming back through the form.Which identifiers those are is knowable only from how the value was built, so
TemplateContextrecords them as it mints them, kept apart from the introduced IRIs.What is deliberately not checked
A blanket check over introduced IRIs breaks working flows, so four cases are exempt:
"Defining an open-ended Space with existing URI"exists precisely for thisdocs/fill-modes.md)The carve-outs are drawn from the published templates rather than guessed. A survey of every template combining
nt:IntroducedResourcewithnt:hasPrefixreturns 30 prefix/placeholder-type pairs: all are genuine minting cases (spaces, I-ADOPT variables, Nanodash projects, 3PFF,~~SPACE~~/) except the 17 AIDA templates, which are allAutoEscapeUriPlaceholder. The onehttps://orcid.org/hit is the superseded "Introducing a user" template; the current one — the one Nanodash links to — uses a prefix-less agent placeholder, so re-introducing yourself with a new key is unaffected.A query service that cannot be reached answers "not taken" and logs: a check that cannot be made is not evidence of a collision, and publishing shouldn't depend on the query services being up.
Testing
./mvnw -o test→ 1243 tests, 0 failures.PrefixMintedIdTest(8 tests): one per row of the table above, plus the guard itself (taken / free / supersede-asks-nothing).QueryApiAccessTestfor the lookup, including the fail-open path.get-introducing-nanopubseparates a taken space IRI (2 rows) from a free one (0 rows).🤖 Generated with Claude Code
https://claude.ai/code/session_01K55p8T6uQoSv48C5SXueve